﻿using UnityEditor;
using UnityEngine;

using System;
using System.IO;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace Hont.C2UTool
{
    public class C2UEntry : EditorWindow
    {
        bool mIsDebugMode;
        bool mIsDeleteOldAnimation;
        bool mIsEnableAdvancedOptionTog;
        int mToolbarIndex;
        int mToolsToolbarIndex;
        int mArgExporterToolbarIndex;
        string mFileName = "";
        string mAnimationName = "";
        string mFinalPath = "";

        float mScaleSliderMin = 0.1f;
        float mScaleSliderMax = 20f;
        float mScaleCurrentValue = 1f;
        Vector3? mScaleValueMark;


        [MenuItem("Hont Tools/Import Middlewave/C4D2Unity3D")]
        public static void ExecuteFunc()
        {
            var window = EditorWindow.GetWindow(typeof(C2UEntry));
            window.titleContent = new GUIContent("C2U Tool");
        }

        void Update()
        {
            Repaint();
        }

        void OnGUI()
        {
            mToolbarIndex = GUILayout.Toolbar(mToolbarIndex
                , new GUIContent[] { new GUIContent("Analyztion"), new GUIContent("Tools"), new GUIContent("Open To C4D(Win)") });

            switch (mToolbarIndex)
            {
                case 0:
                    AnalyzeGUIRefresh();
                    break;

                case 1:
                    ToolsGUIRefresh();
                    break;

                case 2:
                    OpenToC4DRefresh();
                    break;
            }
        }

        #region ---Analyze Tab---
        void AnalyzeGUIRefresh()
        {
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("?", GUILayout.Width(20), GUILayout.Height(14)))
            {
                EditorUtility.DisplayDialog("Debug Mode Help", "Debug mode will saved 'clipboard data and 'c4d middle data'", "OK");
            }
            mIsDebugMode = EditorGUILayout.Toggle("IsDebug", mIsDebugMode);
            EditorGUILayout.EndHorizontal();

            mIsEnableAdvancedOptionTog = EditorGUILayout.Foldout(mIsEnableAdvancedOptionTog, "Advance Option");

            if (mIsEnableAdvancedOptionTog)
            {
                EditorGUILayout.BeginVertical(GUI.skin.box);
                mIsDeleteOldAnimation = EditorGUILayout.Toggle("Delete Old Animation File", mIsDeleteOldAnimation);
                EditorGUILayout.EndVertical();
            }

            var str = C2UUtility.GetFromClipboard();
            if (!string.IsNullOrEmpty(str) && str.Contains("C4DData="))
            {
                if (C2UUtility.ColorButton("Found Data! Analyze>>>", Color.red))
                {
                    C2UUtility.CopyToClipboard("");
                    var path = str.Remove(0, "C4DData=".Length);

                    var attachInfo = File.ReadAllText(path);

                    if (mIsDebugMode)
                    {
                        var finalPath = C2UUtility.CombinePath(C2UUtility.GetDirectory(path), "Debug_Clipboard.txt");

                        if (File.Exists(finalPath)) File.Delete(finalPath);
                        File.AppendAllText(finalPath, str);
                    }
                    else
                    {
                        File.Delete(path);
                    }

                    var context = C2UAnalyzer.InitContext(attachInfo);

                    if (mIsDeleteOldAnimation)
                    {
                        var clipName = context.BaseInfo.FBXPath.Contains("@") ? C2UUtility.LastSubstring(context.BaseInfo.FBXPath, "@") : null;
                        if (!string.IsNullOrEmpty(clipName))
                        {
                            var dir = context.ContextInfo.DirectoryPath;
                            var finalPath = C2UUtility.CombinePath(dir, clipName + ".anim");
                            File.Delete(finalPath);
                        }
                    }

                    C2UAnalyzer.BuildFromStruct(context);
                    C2UAnalyzer.DisposeContext(context);
                }
            }
        }
        #endregion

        #region --- Tools Tab---

        void ToolsGUIRefresh()
        {
            mToolsToolbarIndex = GUILayout.Toolbar(mToolsToolbarIndex, new GUIContent[] {
            new GUIContent("PathBuilder")
            , new GUIContent("AnimClip")
            , new GUIContent("ScaleTester")
            , new GUIContent("ArgExporter") });

            switch (mToolsToolbarIndex)
            {
                case 0:

                    PathBuilderContentRefresh();
                    break;

                case 1:

                    AnimationClipContentRefresh();
                    break;

                case 2:

                    ScaleTesterContentRefresh();
                    break;

                case 3:

                    mArgExporterToolbarIndex = GUILayout.Toolbar(mArgExporterToolbarIndex, new GUIContent[] {
                    new GUIContent("Script Argument Export")
                    , new GUIContent("Shader Argument Export") });

                    switch (mArgExporterToolbarIndex)
                    {
                        case 0:
                            ScriptArgumentContentRefresh();
                            break;

                        case 1:
                            ShaderArgumentContentRefresh();
                            break;
                    }

                    break;
            }
        }

        void PathBuilderContentRefresh()
        {
            mFileName = EditorGUILayout.TextField("File Name(no ext): ", mFileName);
            mAnimationName = EditorGUILayout.TextField("Animation Name: ", mAnimationName);
            mFinalPath = EditorGUILayout.TextField("Final Path: ", mFinalPath);

            if (GUILayout.Button("Get Current Selection Path(Auto combine file name)"))
            {
                var absPath = C2UUtility.GetAssetsAbsolutePath(Selection.activeObject);

                mFinalPath = string.Format("{0}/{1}/{2}.fbx"
                    , absPath
                    , mFileName
                    , string.IsNullOrEmpty(mAnimationName) ? mFileName : mFileName + "@" + mAnimationName);
            }

            if (GUILayout.Button("Copy Path To Clipboard"))
            {
                C2UUtility.CopyToClipboard(mFinalPath);
            }
        }

        void AnimationClipContentRefresh()
        {
            var animClip = C2UUtility.TmpObjectField<AnimationClip>(50, "AnimationClip");

            if (animClip == null) return;

            GUILayout.BeginVertical(GUI.skin.box);
            GUILayout.Label("File Example: _con_|Ghost|TestNode");
            var removeCurveConf = C2UUtility.TmpObjectField<TextAsset>(49, "Remove Curve Conf(*.txt)");
            GUILayout.EndVertical();

            if (GUILayout.Button("Keyframe Reduction"))
            {
                var keywordConfList = new List<string>();
                if (removeCurveConf != null)
                {
                    keywordConfList.AddRange(removeCurveConf.text.Split('|'));
                }

                var oldSize = new FileInfo(C2UUtility.GetAssetsAbsolutePath(animClip)).Length;
                var bindings = AnimationUtility.GetCurveBindings(animClip);

                for (int i = 0; i < bindings.Length; i++)
                {
                    EditorUtility.DisplayProgressBar("Reducting", "", (i / (float)bindings.Length));

                    var curve = AnimationUtility.GetEditorCurve(animClip, bindings[i]);

                    if (keywordConfList.Any(m => bindings[i].path.Contains(m)))
                    {
                        AnimationUtility.SetEditorCurve(animClip, bindings[i], null);//execute remove curve.
                        Debug.Log("Removed curve: " + bindings[i].path);
                        continue;
                    }

                    if (bindings[i].propertyName.StartsWith("m_LocalRotation")) continue;//Not support rotation reduction.

                    var keysList = curve.keys.ToList();
                    for (int j = keysList.Count - 1; j >= 0; j--)
                    {
                        var flag = true;

                        if (j == 0) continue;
                        if (j == keysList.Count - 1) continue;

                        if (j - 1 > -1)
                        {
                            if (keysList[j - 1].value != keysList[j].value) flag = false;
                        }

                        if (j + 1 < keysList.Count)
                        {
                            if (keysList[j + 1].value != keysList[j].value) flag = false;
                        }

                        if (flag)
                        {
                            keysList.RemoveAt(j);
                        }
                    }

                    curve.keys = keysList.ToArray();

                    AnimationUtility.SetEditorCurve(animClip, bindings[i], curve);
                }

                EditorUtility.ClearProgressBar();
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();

                var newSize = new FileInfo(C2UUtility.GetAssetsAbsolutePath(animClip)).Length;

                Debug.Log(string.Format("AniamtionClip Compress Success OldSize:{0} NewSize:{1}", C2UUtility.ConvertByteSizeDisplay(oldSize), C2UUtility.ConvertByteSizeDisplay(newSize)));
            }
        }

        void ScaleTesterContentRefresh()
        {
            if (GUILayout.Button("\nApply\n"))
            {
                if (!mScaleValueMark.HasValue)
                {
                    EditorUtility.DisplayDialog("Tip", "Please Clicked Mark Button!", "OK");
                    return;
                }
                Undo.RecordObject(Selection.activeGameObject.transform, "C4D2Unity_Scale");
                Selection.activeGameObject.transform.localScale = mScaleValueMark.GetValueOrDefault() * mScaleCurrentValue;
            }

            mScaleCurrentValue = EditorGUILayout.Slider(mScaleCurrentValue, mScaleSliderMin, mScaleSliderMax);
            GUILayout.BeginHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("MinValue", GUILayout.MaxWidth(80));
            EditorGUILayout.FloatField(mScaleSliderMin);
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("MaxValue", GUILayout.MaxWidth(80));
            EditorGUILayout.FloatField(mScaleSliderMax);
            GUILayout.EndHorizontal();

            GUILayout.EndHorizontal();

            EditorGUILayout.Space();

            EditorGUILayout.LabelField(string.Format("Final Scale: {0}x", mScaleCurrentValue));

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Clear"))
            {
                mScaleCurrentValue = 1;
                mScaleSliderMin = 0.1f;
                mScaleSliderMax = 20f;
                mScaleValueMark = null;
            }

            if (C2UUtility.ColorButton("Mark", mScaleValueMark.HasValue ? Color.Lerp(GUI.color, Color.red, 0.5f) : GUI.color))
            {
                mScaleValueMark = Selection.activeGameObject.transform.localScale;
            }

            if (GUILayout.Button("Recovery"))
            {
                if (mScaleValueMark.HasValue)
                    Selection.activeGameObject.transform.localScale = mScaleValueMark.Value;
            }
            GUILayout.EndHorizontal();
        }

        void ScriptArgumentContentRefresh()
        {
            GUILayout.Label("-Script Info Export Setting");
            var targetComponent = C2UUtility.TmpObjectField<Component>(51, "Script Object", true);

            if (targetComponent != null)
            {
                GUILayout.BeginVertical(GUI.skin.box);
                var publicFields = C2UUtility.GetObjectPublicFields(targetComponent);
                GUILayout.Label("-Script Name: " + targetComponent.GetType().Name);
                GUILayout.Label("-Field Count: " + publicFields.Length);
                GUILayout.EndVertical();
            }

            if (GUILayout.Button("Export To Clipboard"))
            {
                if (targetComponent != null)
                {
                    var exporterCollection = C2UUtility.GetAssemblyInterfacesAndCreate<IC2UScriptExporter>(this);
                    Array.Sort(exporterCollection, (x, y) => -x.Priority.CompareTo(y.Priority));
                    var targetExporter = exporterCollection.FirstOrDefault(m => m.CanSlover(targetComponent));

                    C2UUtility.CopyToClipboard(targetExporter.Execute(targetComponent));
                }
            }
        }

        void ShaderArgumentContentRefresh()
        {
            GUILayout.Label("-Shader Info Export Setting");
            var mat = C2UUtility.TmpObjectField<Material>(52, "Material Object", false);
            var fieldArr = new ShaderInfoHelper.ShaderProperty[0];

            if (mat != null)
            {
                fieldArr = ShaderInfoHelper.GetFields(mat);
                GUILayout.BeginVertical(GUI.skin.box);
                GUILayout.Label("-Shader Name: " + mat.shader);

                if (fieldArr == null)
                {
                    GUILayout.Label("Can't support this shader");
                    return;
                }

                GUILayout.Label("-Field Count: " + fieldArr.Length);
                GUILayout.EndVertical();

                if (GUILayout.Button("Export To Clipboard"))
                {
                    var str = "";
                    str = C2UUtility.ConvertSecurityString(mat.shader.name) + "*";

                    for (int i = 0; i < fieldArr.Length; i++)
                    {
                        var item = fieldArr[i];
                        var type = "";

                        switch (item.PropType)
                        {
                            case ShaderInfoHelper.ShaderPropertyTypeEnum.Tex2D:
                                type = "texture";
                                var texInfo = (ShaderInfoHelper.ShaderTextureInfo)item.Value;
                                item.Value = string.Format("{0}_{1}_{2}_{3}", texInfo.Offset.x, texInfo.Offset.y, texInfo.Scale.x, texInfo.Scale.y);
                                break;
                            case ShaderInfoHelper.ShaderPropertyTypeEnum.TexCube:
                                type = "cubemap";
                                break;
                            case ShaderInfoHelper.ShaderPropertyTypeEnum.Color:
                                type = "color";
                                var color = (Color)item.Value;
                                item.Value = string.Format("{0}_{1}_{2}_{3}", color.r, color.g, color.b, color.a);
                                break;
                            case ShaderInfoHelper.ShaderPropertyTypeEnum.Vector:
                                type = "vector4";
                                var vec = (Vector4)item.Value;
                                item.Value = string.Format("{0}_{1}_{2}_{3}", vec.x, vec.y, vec.z, vec.w);
                                break;
                            case ShaderInfoHelper.ShaderPropertyTypeEnum.Float:
                                type = "float";
                                break;
                            case ShaderInfoHelper.ShaderPropertyTypeEnum.Keyword:
                                type = "keyword";
                                break;
                            default:
                                break;
                        }

                        if (i > 0 && !string.IsNullOrEmpty(str)) str += "&";
                        var outputName = C2UUtility.ConvertSecurityString(item.Name);
                        var outputType = type;
                        var outputValue = item.Value == null ? "" : C2UUtility.ConvertSecurityString(item.Value.ToString());

                        str += string.Format("Name:{0}@Type:{1}@Value:{2}", outputName, outputType, outputValue);
                    }

                    C2UUtility.CopyToClipboard(str);
                }
            }
        }

        #endregion

        #region ---Open To C4D Tab---
        void OpenToC4DRefresh()
        {
            var linkInfoArr = Selection.objects.Where(m => m.name == "LinkInfo").ToArray();

            if (linkInfoArr == null) linkInfoArr = new UnityEngine.Object[0];

            if (Selection.activeObject != null && Selection.activeObject.name == "LinkInfo")
            {
                GUILayout.Label("Please Click Open");
            }
            else
            {
                GUILayout.Label("Please Select Link File");
            }

            if (GUILayout.Button(string.Format("Open({0})", linkInfoArr.Length)))
            {
                var strArr = C2UUtility.ConvertAll(linkInfoArr, m =>
                {
                    var path = C2UUtility.GetDirectory(C2UUtility.GetAssetsAbsolutePath(m));
                    var str = C2UUtility.ReadAssetsFileToString(m);
                    var absolutePath = string.Format("\"{0}\"", C2UUtility.ToAbsolutePath(str, path));
                    return absolutePath;
                })
                .ToArray();

                var t = new System.Threading.Thread(() =>
                {
                    for (int i = 0; i < strArr.Length; i++)
                    {
                        var item = strArr[i];
                        C2UUtility.ProcessExecute(item);
                    }
                });

                t.Start();
                t.Abort();
            }
        }
        #endregion
    }
}
